package com.x.uikit.channel.multitabs;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;

import java.util.ArrayList;
import java.util.List;

public class ScrollableTabLayout extends HorizontalScrollView {
    private LinearLayout mTabsContainer;
    private IIndicator mIndicator;
    private TabListeners mTabListeners;
    private AbstractAdapter<?, ?> mAdapter;
    private float mCurrentPositionOffset;
    private int mLastScrollX;
    private int mCurrentTabIndex;
    private int mOldTabIndex;
    private float mPageScrollDistance;

    public ScrollableTabLayout(Context context) {
        this(context, null, 0);
    }

    public ScrollableTabLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ScrollableTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        setWillNotDraw(false);
        mTabsContainer = new LinearLayout(getContext());
        addView(mTabsContainer, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        mTabListeners = new TabListeners();
        mCurrentTabIndex = 0;
    }


    public void setAdapter(AbstractAdapter<?, ?> adapter) {
        if (mAdapter != null) {
            mAdapter.unregisterDataChangedObserver(mAdapterDataChangedObserver);
        }
        mAdapter = adapter;
        if (adapter != null) {
            adapter.registerDataChangedObserver(mAdapterDataChangedObserver);
            adapter.notifyDataSetChanged();
        }
    }

    private DataChangedObserver mAdapterDataChangedObserver = new DataChangedObserver() {
        @Override
        public void onDataSetChanged() {
            handleDataSetChanged();
        }
    };

    protected void handleDataSetChanged() {
        mTabsContainer.removeAllViews();
        if (mAdapter == null) {
            return;
        }
        final int size = mAdapter.getCount();
        int currentTabIndex = getCurrentTab();
        for (int i = 0; i < size; ++i) {
            final IItemView itemView = mAdapter.getView(getContext(), i);
            if (itemView != null) {
                if (currentTabIndex == i) {
                    itemView.selected();
                } else {
                    itemView.unselected();
                }
                View view = itemView.getView();
                view.setOnClickListener(mClickListener);
                view.setOnLongClickListener(mLongClickListener);
                mTabsContainer.addView(view, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
            }
        }
    }


    private OnClickListener mClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            int position = mTabsContainer.indexOfChild(v);
            int lastTabIndex = getCurrentTab();
            if (mTabListeners.selectedListeners.size() > 0) {
                for (OnTabSelectedListener listener : mTabListeners.selectedListeners) {
                    if (listener != null) {
                        listener.onTabClicked(position, lastTabIndex);
                    }
                }
            }
        }
    };
    private OnLongClickListener mLongClickListener = new OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            int position = mTabsContainer.indexOfChild(v);
            if (mTabListeners.selectedListeners.size() > 0) {
                for (OnTabSelectedListener listener : mTabListeners.selectedListeners) {
                    if (listener != null) {
                        listener.onTabLongClicked(position);
                    }
                }
            }
            return false;
        }

    };

    public int getOldTab() {
        return mOldTabIndex;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (isInEditMode()) {
            return;
        }
        if (mIndicator != null) {
            mIndicator.onDraw(canvas, mTabsContainer, getCurrentTab(), getOldTab(), mCurrentPositionOffset);
        }
    }


    private void updateTabSelection(int position) {
        final int tabCount = mTabsContainer.getChildCount();
        for (int i = 0; i < tabCount; ++i) {
            View tabView = mTabsContainer.getChildAt(i);
            final boolean isSelect = i == position;
            if (tabView instanceof IItemView) {
                if (isSelect) {
                    ((IItemView) tabView).selected();
                } else {
                    ((IItemView) tabView).unselected();
                }
            }
        }
    }


    public int getTabCount() {
        return mAdapter != null ? mAdapter.getCount() : 0;
    }

    private void calculateScrollDistance(int oldTabIndex, int currentTabIndex) {
        mPageScrollDistance = 0;
        mLastScrollX = 0;
        if (mTabsContainer.getMeasuredWidth() <= getWidth() || oldTabIndex == currentTabIndex || mTabsContainer.getChildCount() < 1) {
            return;
        }
        View currentTab = mTabsContainer.getChildAt(currentTabIndex);
        int currentTabCenter = getTabRight(currentTab) - currentTab.getWidth() / 2;
        int center = getWidth() / 2;
        View firstTab = mTabsContainer.getChildAt(0);
        View lastTab = mTabsContainer.getChildAt(mTabsContainer.getChildCount() - 1);
        if (oldTabIndex < currentTabIndex) {
            if (currentTabCenter > center || getTabLeft(firstTab) < 0) {
                if (getTabRight(lastTab) > getWidth() || center > currentTabCenter) {
                    mPageScrollDistance = currentTabCenter - center;
                }
            }
        } else {
            if (currentTabCenter < center || getTabRight(lastTab) > getWidth()) {
                if (getTabLeft(firstTab) < 0 || center < currentTabCenter) {
                    mPageScrollDistance = currentTabCenter - center;
                }
            }
        }
    }

    private int getTabLeft(View tab) {
        return tab.getLeft() - getScrollX();
    }

    private int getTabRight(View tab) {
        return tab.getRight() - getScrollX();
    }

    public void onPageSelected(int position) {
        this.mCurrentTabIndex = position;
        updateTabSelection(position);
        invalidate();
    }

    public void onPageScrolled(int oldTabIndex, int currentTabIndex, float positionOffset) {
        if (oldTabIndex != mOldTabIndex || currentTabIndex != mCurrentTabIndex) {
            calculateScrollDistance(oldTabIndex, currentTabIndex);
        }
        this.mCurrentTabIndex = currentTabIndex;
        this.mOldTabIndex = oldTabIndex;
        this.mCurrentPositionOffset = positionOffset;
        IItemView currentItemView = getItemView(currentTabIndex);
        if (currentItemView != null) {
            currentItemView.setSlideProgress(positionOffset);
        }
        IItemView oldItemView = getItemView(oldTabIndex);
        if (oldItemView != null) {
            oldItemView.setSlideProgress(1 - positionOffset);
        }
        if (mTabsContainer.getMeasuredWidth() <= getWidth() || mOldTabIndex == mCurrentTabIndex) {
            return;
        }
        int offset = (int) (mCurrentPositionOffset * mPageScrollDistance);
        int newScrollX = offset - mLastScrollX;
        mLastScrollX = offset;
        smoothScrollBy(newScrollX, 0);
        invalidate();
    }

    public IItemView getItemView(int position) {
        final int tabCount = mTabsContainer.getChildCount();
        if (position >= 0 && position < tabCount) {
            View nextView = mTabsContainer.getChildAt(position);
            if (nextView instanceof IItemView) {
                return (IItemView) nextView;
            }
        }
        return null;
    }


    public int getCurrentTab() {
        return mCurrentTabIndex;
    }

    public IItemView getCurrentTabView() {
        return getItemView(getCurrentTab());
    }

    public void setIndicator(IIndicator indicator) {
        this.mIndicator = indicator;
    }

    public static int dpI(float value) {
        return (int) dpF(value);
    }

    public static float dpF(float value) {
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, Resources.getSystem().getDisplayMetrics());
    }


    public interface IItemView {
        void selected();

        void unselected();

        View getView();

        /**
         * page切换滑动进度
         *
         * @param value 0-1
         */
        void setSlideProgress(float value);
    }


    public abstract static class AbstractAdapter<Tab, ItemView extends IItemView> {
        protected List<Tab> mTabList;
        protected List<DataChangedObserver> mDataChangedObservers;

        public AbstractAdapter() {
            mDataChangedObservers = new ArrayList<>();
        }

        public void setTabList(List<Tab> mTabList) {
            this.mTabList = mTabList;
        }

        public Tab getItem(int position) {
            if (mTabList != null && position >= 0 && position < getCount()) {
                return mTabList.get(position);
            }
            return null;
        }

        public int getItemPosition(Tab tab) {
            if (tab == null || mTabList == null) {
                return -1;
            }
            return mTabList.indexOf(tab);
        }

        public abstract int getCount();

        public abstract ItemView getView(Context context, int position);

        public void notifyDataSetChanged() {
            if (mDataChangedObservers.size() > 0) {
                for (DataChangedObserver observer : mDataChangedObservers) {
                    if (observer != null) {
                        observer.onDataSetChanged();
                    }
                }
            }
        }

        public void registerDataChangedObserver(DataChangedObserver observer) {
            if (observer != null) {
                mDataChangedObservers.add(observer);
            }
        }

        public void unregisterDataChangedObserver(DataChangedObserver observer) {
            if (observer != null) {
                mDataChangedObservers.remove(observer);
            }
        }
    }

    public void addOnTabSelectedListener(OnTabSelectedListener listener) {
        mTabListeners.addOnTabSelectedListener(listener);
    }

    public void removeOnTabSelectedListener(OnTabSelectedListener listener) {
        mTabListeners.removeOnTabSelectedListener(listener);
    }

    private static class TabListeners {
        ArrayList<OnTabSelectedListener> selectedListeners;

        public TabListeners() {
            selectedListeners = new ArrayList<>();
        }

        public void addOnTabSelectedListener(OnTabSelectedListener listener) {
            if (listener != null) {
                selectedListeners.add(listener);
            }
        }

        public void removeOnTabSelectedListener(OnTabSelectedListener listener) {
            if (listener != null) {
                selectedListeners.remove(listener);
            }
        }


    }

    public interface IIndicator {
        void onDraw(Canvas canvas, ViewGroup tabContainer, int currentPosition, int lastPosition, float currentPositionOffset);
    }

    public interface OnTabSelectedListener {
        void onTabClicked(int newIndex, int oldIndex);

        void onTabLongClicked(int index);
    }

    public static abstract class OnTabSelectedListenerAdapter implements OnTabSelectedListener {
        @Override
        public void onTabClicked(int newIndex, int oldIndex) {

        }

        @Override
        public void onTabLongClicked(int index) {

        }
    }

    public interface DataChangedObserver {
        void onDataSetChanged();
    }
}
